package main

/**
系统监控功能服务
*/
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"monitors/model"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

type Server int

var (
	resultSize = 1024 * 512
)

func (t *Server) Upload(args map[string][]byte, msg *string) error {
	var s = model.AppInfo{}
	json.Unmarshal(args["s"], &s)
	data := args["data"]
	filename := string(args["filename"])

	if filename == "monitors.zip" {
		ioutil.WriteFile("./data/"+filename, data, 0666)
		*msg = s.Name + "升级成功|"
		return nil
	}

	ioutil.WriteFile("tmp/"+filename, data, 0666)
	dir := path.Dir(s.AppAddr)
	if strings.HasSuffix(filename, ".zip") {
		shell("unzip -o -d " + dir + " ./tmp/" + filename)
	} else {
		fPath := filepath.Join(s.AppAddr, filename)
		_, err := os.Stat(fPath)
		if err != nil {
			shell("mv ./tmp/" + filename + " " + s.AppAddr)
		} else {
			filepath.Walk(dir, func(p string, info os.FileInfo, err error) error {
				if info.Name() != filename {
					return nil
				}
				t.Stop(s, msg)
				time.Sleep(time.Second * 2)
				fdir := path.Dir(p)
				shell("rm -rf " + p)
				fmt.Printf("rm -rf %s\n", p)
				shell("mv ./tmp/" + filename + " " + fdir)
				fmt.Printf("mv ./tmp/%s  %s\n", filename, fdir)
				if path.Ext(filename) == ".war" {
					shell("rm -rf " + strings.TrimSuffix(s.AppAddr, ".war"))
				}
				return nil
			})
		}
	}
	shell("rm -rf ./tmp/*")
	t.Start(s, msg)
	*msg = "上传成功"
	return nil
}

func (t *Server) Start(s model.AppInfo, msg *string) error {
	if strings.HasSuffix(s.RunAddr, ".sh") {
		shell("cd " + path.Dir(s.RunAddr) + " && sh " + path.Base(s.RunAddr))
	} else {
		shell("cd " + path.Dir(s.RunAddr) + " && " + path.Base(s.RunAddr))
	}

	*msg = "服务已启动"
	return nil
}

func (t *Server) Stop(s model.AppInfo, msg *string) error {
	if strings.HasSuffix(s.RunAddr, ".sh") {
		shell("cd " + path.Dir(s.ShutdownAddr) + " && sh " + path.Base(s.ShutdownAddr))
	} else {
		shell("cd " + path.Dir(s.ShutdownAddr) + " && " + path.Base(s.ShutdownAddr))
	}
	*msg = "服务已停止"
	return nil
}

func (t *Server) Logs(params map[string][]byte, context *map[string]interface{}) error {
	s := model.AppInfo{}
	json.Unmarshal(params["s"], &s)
	date := string(params["date"])
	size, _ := strconv.Atoi(string(params["size"]))
	if strings.Contains(s.LogsAddr, "{date}") {
		s.LogsAddr = strings.Replace(s.LogsAddr, "{date}", date, -1)
	}
	f, err := os.Open(s.LogsAddr)
	f.Seek(int64(size), 0)
	if err != nil {
		(*context)["size"] = size
		(*context)["data"] = err.Error()
		return err
	}
	defer f.Close()
	d := make([]byte, resultSize)
	n, _ := f.Read(d)
	buf := bytes.NewBuffer(d[:n])
	result := bytes.Buffer{}
	for {
		line, err := buf.ReadBytes('\n')
		if err != nil {
			break
		}
		size = size + len(line)
		data := string(line)
		data = strings.Replace(data, "<", "&lt;", -1)
		data = strings.Replace(data, ">", "&gt;", -1)
		result.WriteString("<p>")
		result.WriteString(data)
		result.WriteString("</p>")
	}
	(*context)["size"] = size
	(*context)["data"] = result.String()

	return nil
}

func (t *Server) Clear(params map[string][]byte, context *string) error {
	s := model.AppInfo{}
	json.Unmarshal(params["s"], &s)
	date := string(params["date"])
	if strings.Contains(s.LogsAddr, "{date}") {
		s.LogsAddr = strings.Replace(s.LogsAddr, "{date}", date, -1)
	}
	os.Truncate(s.LogsAddr, 0)
	*context = "日志已清除"
	return nil
}

type LogInit int

func (LogInit) pathExists(path string) bool {
	_, err := os.Stat(path)
	if os.IsNotExist(err) {
		return true
	}
	return false
}

func (LogInit) writeFile(path string, buf bytes.Buffer) {
	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.ModePerm)
	if err != nil {
		return
	}
	f.Seek(0, io.SeekEnd)
	buf.WriteTo(f)
	f.Close()
}

func main() {
	os.Mkdir("tmp", 0777)
	os.Mkdir("data", 0777)
	rpc.Register(new(Server))
	lis, err := net.Listen("tcp", ":"+os.Args[1])
	if err != nil {
		return
	}
	defer lis.Close()
	for {
		conn, err := lis.Accept()
		if err != nil {
			log.Fatalf("lis.Accept(): %v\n", err)
		}
		go jsonrpc.ServeConn(conn)
	}
}

func shell(s string) (string, error) {
	cmd := exec.Command("/bin/bash", "-c", s)

	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()

	return out.String(), err
}
